﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Design;
using Needle.Framework;
using System.Collections;
using Needle.DevToolkit.Win32;
using Needle.Utils.Extensions;

namespace Needle.DevToolkit.Services.UI
{
    /// <summary>
    /// implements the ISelectionService interface, reverse engineered from
    /// System.ComponentModel.Design.SelectionService.
    /// </summary>
    public class SelectionService : ServiceBase, ISelectionService
    {
        private static readonly object[] EmptySelection = new object[] { };

        List<object> _selection;

        public SelectionService(IServiceProvider serviceProvider)
            : base(serviceProvider)
        {
        }

        public event EventHandler SelectionChanged;

        public event EventHandler SelectionChanging;

        public int SelectionCount
        {
            get { return _selection == null ? 0 : _selection.Count; }
        }

        public object PrimarySelection
        {
            get { return SelectionCount == 0 ? null : _selection[0]; }
        }

        public bool GetComponentSelected(object component)
        {
            return SelectionCount == 0 ? false : _selection.Contains(component);
        }

        public ICollection GetSelectedComponents()
        {
            return SelectionCount == 0 ? EmptySelection : _selection.ToArray();
        }

        public void SetSelectedComponents(ICollection components)
        {
            SetSelectedComponents(components, SelectionTypes.Auto);
        }

        public void SetSelectedComponents(ICollection components, SelectionTypes selectionType)
        {

            bool toggle = (selectionType & SelectionTypes.Toggle) == SelectionTypes.Toggle;
            bool primary = (selectionType & SelectionTypes.Primary) == SelectionTypes.Primary;
            bool add = (selectionType & SelectionTypes.Add) == SelectionTypes.Add;
            bool remove = (selectionType & SelectionTypes.Remove) == SelectionTypes.Remove;
            bool replace = (selectionType & SelectionTypes.Replace) == SelectionTypes.Replace;
            bool auto = !(toggle | add | remove | replace);

            if (components == null)
                components = EmptySelection;

            if (auto)
            {
                toggle = Keyboard.CtrlKeyDown || Keyboard.ShiftKeyDown;
                add |= Keyboard.ShiftKeyDown;
                if (toggle || add)
                {
                    primary = false;
                }
            }

            object primaryComponent = null;
            if (primary && components.Count == 1)
            {
                foreach (object component in components)
                {
                    primaryComponent = component;
                    if (component == null)
                        throw new ArgumentNullException("components");

                    break;
                }
            }

            int primaryIndex = _selection == null ? -1 : _selection.IndexOf(primaryComponent);
            bool selectionChanged = false;
            if (primaryComponent != null && _selection != null && primaryIndex != -1)
            {
                if (primaryIndex != 0)
                {
                    object oldPrimaryComponent = _selection[0];
                    _selection[0] = _selection[primaryIndex];
                    _selection[primaryIndex] = oldPrimaryComponent;
                    selectionChanged = true;
                }
            }
            else
            {
                if (_selection != null && !toggle && !add && !remove)
                {
                    object[] selection = new object[_selection.Count];
                    _selection.CopyTo(selection, 0);
                    foreach (object selectedComponent in selection)
                    {
                        bool removeComponent = true;
                        foreach (object component in components)
                        {
                            if (component == null)
                            {
                                throw new ArgumentNullException("components");
                            }
                            if (object.ReferenceEquals(component, selectedComponent))
                            {
                                removeComponent = false;
                                break;
                            }
                        }
                        if (removeComponent)
                        {
                            RemoveSelection(selectedComponent);
                            selectionChanged = true;
                        }
                    }
                }

                foreach (object component in components)
                {
                    if (component == null)
                    {
                        throw new ArgumentNullException("components");
                    }
                    if (_selection != null && _selection.Contains(component))
                    {
                        if (toggle || remove)
                        {
                            RemoveSelection(component);
                            selectionChanged = true;
                        }
                        continue;
                    }
                    if (!remove)
                    {
                        AddSelection(component);
                        selectionChanged = true;
                    }
                }
            }
            if (selectionChanged)
            {
                OnSelectionChanged();
            }
        }

        protected virtual void OnSelectionChanged()
        {
            OnSelectionChanging();
            var handler = SelectionChanged;
            if (handler != null)
            {
                try
                {
                    handler(this, EventArgs.Empty);
                }
                catch
                {
                }
            }
        }

        protected virtual void OnSelectionChanging()
        {
            var handler = SelectionChanging;
            if (handler != null)
                handler(this, EventArgs.Empty);
        }

        private void AddSelection(object component)
        {
            if (_selection == null)
                _selection = new List<object>();

            if (!_selection.Contains(component))
                _selection.Add(component);
        }

        private void RemoveSelection(object component)
        {
            if (_selection != null)
                _selection.Remove(component);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                SetSelectedComponents(EmptySelection, SelectionTypes.Replace);
            }
            base.Dispose(disposing);
        }


        protected override Type[] ExposedServices
        {
            get { return new Type[] { typeof(ISelectionService) }; }
        }
    }
}
